/*
 * AllerTestView.java
 */

package allertest;

import blobDetection.EdgeVertex;
import it.allerj.analysis.BlobDummyFilter;
import it.allerj.analysis.BlobFoodFilter;
import it.allerj.analysis.DotMark;
import it.allerj.analysis.FilmAnalyzer;
import it.allerj.analysis.SamplingCell;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import org.jdesktop.application.Action;
import org.jdesktop.application.SingleFrameApplication;
import org.jdesktop.application.FrameView;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

/**
 * The application's main frame.
 */
public class AllerTestView extends FrameView {
    
    public AllerTestView(SingleFrameApplication app) {
        super(app);

        initComponents();
        
        // configure log handler
        TextAreaLogHandler tal = new TextAreaLogHandler(logArea);
        logger = Logger.getLogger("AllerTest");
        logger.addHandler(tal);
        logger.info("Log inizializzato...");
        
        allerJlogger = Logger.getLogger("AllerJ");
        allerJlogger.addHandler(tal);
    }

    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = AllerTestApp.getApplication().getMainFrame();
            aboutBox = new AllerTestAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        AllerTestApp.getApplication().show(aboutBox);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        textFieldImagePath = new javax.swing.JTextField();
        jLabel1 = new javax.swing.JLabel();
        btnBrowse = new javax.swing.JButton();
        btnAnalyze = new javax.swing.JButton();
        jPanel1 = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();
        spinSquareSimilarity = new javax.swing.JSpinner();
        spinStopThreshold = new javax.swing.JSpinner();
        spinStartThreshold = new javax.swing.JSpinner();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        spinRefThreshold = new javax.swing.JSpinner();
        spinSteps = new javax.swing.JSpinner();
        jLabel7 = new javax.swing.JLabel();
        spinCircleSimilarity1 = new javax.swing.JSpinner();
        jLabel8 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jLabel4 = new javax.swing.JLabel();
        jLabel9 = new javax.swing.JLabel();
        spinMaxArea = new javax.swing.JSpinner();
        spinCols = new javax.swing.JSpinner();
        jLabel10 = new javax.swing.JLabel();
        jLabel11 = new javax.swing.JLabel();
        spinMinArea1 = new javax.swing.JSpinner();
        spinRows = new javax.swing.JSpinner();
        cbShowBlobs = new javax.swing.JCheckBox();
        cbShowBBox = new javax.swing.JCheckBox();
        cbShowGrid = new javax.swing.JCheckBox();
        jLabel12 = new javax.swing.JLabel();
        spinColsToAnalyze = new javax.swing.JSpinner();
        spinRowsToAnalyze = new javax.swing.JSpinner();
        jLabel13 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        logArea = new javax.swing.JTextArea();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();

        mainPanel.setMaximumSize(new java.awt.Dimension(465, 429));
        mainPanel.setMinimumSize(new java.awt.Dimension(465, 429));
        mainPanel.setName("mainPanel"); // NOI18N
        mainPanel.setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(allertest.AllerTestApp.class).getContext().getResourceMap(AllerTestView.class);
        textFieldImagePath.setText(resourceMap.getString("textFieldImagePath.text")); // NOI18N
        textFieldImagePath.setName("textFieldImagePath"); // NOI18N
        mainPanel.add(textFieldImagePath, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 340, 450, -1));

        jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
        jLabel1.setName("jLabel1"); // NOI18N
        mainPanel.add(jLabel1, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 350, -1, -1));

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(allertest.AllerTestApp.class).getContext().getActionMap(AllerTestView.class, this);
        btnBrowse.setAction(actionMap.get("browse")); // NOI18N
        btnBrowse.setText(resourceMap.getString("btnBrowse.text")); // NOI18N
        btnBrowse.setName("btnBrowse"); // NOI18N
        mainPanel.add(btnBrowse, new org.netbeans.lib.awtextra.AbsoluteConstraints(540, 340, -1, -1));

        btnAnalyze.setAction(actionMap.get("startAnalyze")); // NOI18N
        btnAnalyze.setText(resourceMap.getString("btnAnalyze.text")); // NOI18N
        btnAnalyze.setName("btnAnalyze"); // NOI18N
        mainPanel.add(btnAnalyze, new org.netbeans.lib.awtextra.AbsoluteConstraints(630, 340, -1, -1));

        jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(resourceMap.getString("jPanel1.border.title"))); // NOI18N
        jPanel1.setName("jPanel1"); // NOI18N
        jPanel1.setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
        jLabel2.setName("jLabel2"); // NOI18N
        jPanel1.add(jLabel2, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 20, -1, -1));

        spinSquareSimilarity.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.3f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.1f)));
        spinSquareSimilarity.setName("spinSquareSimilarity"); // NOI18N
        jPanel1.add(spinSquareSimilarity, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 130, 60, -1));

        spinStopThreshold.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.96f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.01f)));
        spinStopThreshold.setName("spinStopThreshold"); // NOI18N
        jPanel1.add(spinStopThreshold, new org.netbeans.lib.awtextra.AbsoluteConstraints(590, 50, 60, -1));

        spinStartThreshold.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.8f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.01f)));
        spinStartThreshold.setName("spinStartThreshold"); // NOI18N
        jPanel1.add(spinStartThreshold, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 50, 60, -1));

        jLabel5.setText(resourceMap.getString("jLabel5.text")); // NOI18N
        jLabel5.setName("jLabel5"); // NOI18N
        jPanel1.add(jLabel5, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 100, -1, -1));

        jLabel6.setText(resourceMap.getString("jLabel6.text")); // NOI18N
        jLabel6.setName("jLabel6"); // NOI18N
        jPanel1.add(jLabel6, new org.netbeans.lib.awtextra.AbsoluteConstraints(350, 180, -1, -1));

        spinRefThreshold.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.3f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.01f)));
        spinRefThreshold.setName("spinRefThreshold"); // NOI18N
        jPanel1.add(spinRefThreshold, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 10, 60, -1));

        spinSteps.setModel(new javax.swing.SpinnerNumberModel(15, 1, 20, 1));
        spinSteps.setName("spinSteps"); // NOI18N
        jPanel1.add(spinSteps, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 90, 60, -1));

        jLabel7.setText(resourceMap.getString("jLabel7.text")); // NOI18N
        jLabel7.setName("jLabel7"); // NOI18N
        jPanel1.add(jLabel7, new org.netbeans.lib.awtextra.AbsoluteConstraints(350, 140, -1, -1));

        spinCircleSimilarity1.setModel(new javax.swing.SpinnerNumberModel(Float.valueOf(0.3f), Float.valueOf(0.0f), Float.valueOf(1.0f), Float.valueOf(0.1f)));
        spinCircleSimilarity1.setName("spinCircleSimilarity1"); // NOI18N
        jPanel1.add(spinCircleSimilarity1, new org.netbeans.lib.awtextra.AbsoluteConstraints(590, 130, 60, -1));

        jLabel8.setText(resourceMap.getString("jLabel8.text")); // NOI18N
        jLabel8.setName("jLabel8"); // NOI18N
        jPanel1.add(jLabel8, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 140, -1, -1));

        jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
        jLabel3.setName("jLabel3"); // NOI18N
        jPanel1.add(jLabel3, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 60, -1, -1));

        jLabel4.setText(resourceMap.getString("jLabel4.text")); // NOI18N
        jLabel4.setName("jLabel4"); // NOI18N
        jPanel1.add(jLabel4, new org.netbeans.lib.awtextra.AbsoluteConstraints(350, 60, -1, -1));

        jLabel9.setText(resourceMap.getString("jLabel9.text")); // NOI18N
        jLabel9.setName("jLabel9"); // NOI18N
        jPanel1.add(jLabel9, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 180, -1, -1));

        spinMaxArea.setModel(new javax.swing.SpinnerNumberModel(90, 0, 120, 10));
        spinMaxArea.setName("spinMaxArea"); // NOI18N
        jPanel1.add(spinMaxArea, new org.netbeans.lib.awtextra.AbsoluteConstraints(590, 170, 60, -1));

        spinCols.setModel(new javax.swing.SpinnerNumberModel(9, 1, 9, 1));
        spinCols.setName("spinCols"); // NOI18N
        jPanel1.add(spinCols, new org.netbeans.lib.awtextra.AbsoluteConstraints(240, 210, 60, -1));

        jLabel10.setText(resourceMap.getString("jLabel10.text")); // NOI18N
        jLabel10.setName("jLabel10"); // NOI18N
        jPanel1.add(jLabel10, new org.netbeans.lib.awtextra.AbsoluteConstraints(220, 220, -1, -1));

        jLabel11.setText(resourceMap.getString("jLabel11.text")); // NOI18N
        jLabel11.setName("jLabel11"); // NOI18N
        jPanel1.add(jLabel11, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 290, -1, -1));

        spinMinArea1.setModel(new javax.swing.SpinnerNumberModel(25, 0, 100, 10));
        spinMinArea1.setName("spinMinArea1"); // NOI18N
        jPanel1.add(spinMinArea1, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 170, 60, -1));

        spinRows.setModel(new javax.swing.SpinnerNumberModel(9, 1, 9, 1));
        spinRows.setName("spinRows"); // NOI18N
        jPanel1.add(spinRows, new org.netbeans.lib.awtextra.AbsoluteConstraints(150, 210, 60, -1));

        cbShowBlobs.setSelected(true);
        cbShowBlobs.setText(resourceMap.getString("cbShowBlobs.text")); // NOI18N
        cbShowBlobs.setName("cbShowBlobs"); // NOI18N
        jPanel1.add(cbShowBlobs, new org.netbeans.lib.awtextra.AbsoluteConstraints(400, 270, -1, -1));

        cbShowBBox.setSelected(true);
        cbShowBBox.setText(resourceMap.getString("cbShowBBox.text")); // NOI18N
        cbShowBBox.setName("cbShowBBox"); // NOI18N
        jPanel1.add(cbShowBBox, new org.netbeans.lib.awtextra.AbsoluteConstraints(520, 270, -1, -1));

        cbShowGrid.setText(resourceMap.getString("cbShowGrid.text")); // NOI18N
        cbShowGrid.setName("cbShowGrid"); // NOI18N
        jPanel1.add(cbShowGrid, new org.netbeans.lib.awtextra.AbsoluteConstraints(270, 270, -1, -1));

        jLabel12.setText(resourceMap.getString("jLabel12.text")); // NOI18N
        jLabel12.setName("jLabel12"); // NOI18N
        jPanel1.add(jLabel12, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 220, -1, -1));

        spinColsToAnalyze.setModel(new javax.swing.SpinnerNumberModel(9, 1, 9, 1));
        spinColsToAnalyze.setName("spinColsToAnalyze"); // NOI18N
        jPanel1.add(spinColsToAnalyze, new org.netbeans.lib.awtextra.AbsoluteConstraints(170, 280, 60, -1));

        spinRowsToAnalyze.setModel(new javax.swing.SpinnerNumberModel(9, 1, 9, 1));
        spinRowsToAnalyze.setName("spinRowsToAnalyze"); // NOI18N
        jPanel1.add(spinRowsToAnalyze, new org.netbeans.lib.awtextra.AbsoluteConstraints(170, 250, 60, -1));

        jLabel13.setText(resourceMap.getString("jLabel13.text")); // NOI18N
        jLabel13.setName("jLabel13"); // NOI18N
        jPanel1.add(jLabel13, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 260, -1, -1));

        mainPanel.add(jPanel1, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 10, 730, 320));

        jScrollPane1.setName("jScrollPane1"); // NOI18N

        logArea.setColumns(20);
        logArea.setRows(5);
        logArea.setName("logArea"); // NOI18N
        jScrollPane1.setViewportView(logArea);

        mainPanel.add(jScrollPane1, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 380, 730, 200));

        menuBar.setName("menuBar"); // NOI18N

        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        setComponent(mainPanel);
        setMenuBar(menuBar);
    }// </editor-fold>//GEN-END:initComponents

    @Action
    public void browse() {
        JFileChooser fc = new JFileChooser();
        if (fc.showOpenDialog(AllerTestApp.getApplication().getMainFrame()) ==
                JFileChooser.APPROVE_OPTION) {
            textFieldImagePath.setText(fc.getSelectedFile().getAbsolutePath());
        }
    }

    @Action
    public void startAnalyze() {
                
        BufferedImage img;
        
        try {
            File file = new File(textFieldImagePath.getText());
            img = ImageIO.read(file);
        }
        catch (Exception ex) {
            logger.severe("Errore apertura file");
            return;
        }
        
        (new RunTestWorker(this, img)).execute();
    }
    
    public void test(BufferedImage img) {
   
        int matrixRows = (Integer) spinRows.getValue();
        int matrixCols = (Integer) spinCols.getValue();
        
        // instance a FilmAnalyzer and INIT! pass a dummy filter, it's enough...
        FilmAnalyzer analyzer = new FilmAnalyzer(img, matrixRows, matrixCols);
        
        // configure analyzer
        analyzer.refPointThreshold = (Float) spinRefThreshold.getValue();
        analyzer.foodPointThresholdStart = (Float) spinStartThreshold.getValue();
        analyzer.foodPointThresholdEnd = (Float) spinStopThreshold.getValue();
        analyzer.foodPointSamplingSteps = (Integer) spinSteps.getValue();
        analyzer.circularityTolerance = (Float) spinCircleSimilarity1.getValue();
                
        // init analysis system
        if (!analyzer.init( new BlobDummyFilter() )) {
            logger.severe("Unable to find black reference points!");
            return;
        }
        
        // get actual image after init
        img = analyzer.getImage();
        
        // configure a suitable filter to discard bad blobs
        // instance a Blob filtering object to discard bad blobs
        BlobFoodFilter filter = new BlobFoodFilter();
        
        // set blob filter global configuration
        filter.whRatioTolerance = (Float) spinSquareSimilarity.getValue();
        filter.minAreaTolerancePerc = (Integer) spinMinArea1.getValue();
        filter.maxAreaTolerancePerc = (Integer) spinMaxArea.getValue();
        
        // for every cell, retrieve mark if one was found        
        int rows = (Integer)spinRowsToAnalyze.getValue();
        int cols = (Integer)spinColsToAnalyze.getValue();
        ArrayList<DotMark> markersList = new ArrayList<DotMark>();
        
        for (int i = 1; i < rows; i++) {
            for (int j = 0; j < cols; j++) {                   
                logger.info("Analisi cella: ("+i+","+j+")");
                
                // retrieve image area where to find dot mark
                SamplingCell sc = analyzer.getSamplingCell(i, j);
                BufferedImage cellImage = sc.image;
                
                // refine filter configuration
                filter.imageWidth = cellImage.getWidth();
                filter.imageHeight = cellImage.getHeight();

                // retrieve dotmark and store it (could be an empty DotMark)
                DotMark dm = analyzer.getFoodDotMark(cellImage, filter); 
                dm.cropTop = sc.cropTop;
                dm.cropBottom = sc.cropBottom;
                markersList.add(dm);               
            }
        }                
        
        if (cbShowBlobs.isSelected()) {
            paintMarkers(img, markersList, true);
        }
        
        if (cbShowBBox.isSelected()) {
            paintMarkers(img, markersList, false);
        }
        
        if (cbShowGrid.isSelected()) {
            paintGrid(img, markersList);
        }
        
        showImageJFrame(img);
    }
           
    private void paintGrid(BufferedImage image, 
            ArrayList<DotMark> pointList) {
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.blue);
        for (DotMark d : pointList) {
            float widthA = d.getCropTop().x;
            float heightA = d.getCropTop().y;
            float widthB = d.getCropBottom().x;
            float heightB = d.getCropBottom().y;
            g2d.draw(new Line2D.Float(widthA, heightA, widthB, heightA));
            g2d.draw(new Line2D.Float(widthB, heightA, widthB, heightB));
            g2d.draw(new Line2D.Float(widthB, heightB, widthA, heightB));
            g2d.draw(new Line2D.Float(widthA, heightB, widthA, heightA));
        }
        g2d.dispose();
    }
    
    private void paintMarkers(BufferedImage image, 
            ArrayList<DotMark> pointList, boolean byEdges) {
        Graphics2D g2d = image.createGraphics();
        g2d.setColor(Color.green);
        for (DotMark d : pointList) {
            if (byEdges) {                
                g2d.setColor(Color.green);
                ArrayList<EdgeVertex> v = d.getVertexes();
                for (int i=0; i<v.size()-1; ++i) {
                    Point.Float p1 = new Point.Float(v.get(i).x, v.get(i).y);
                    Point.Float p2 = new Point.Float(v.get(i+1).x, v.get(i+1).y);
                    p1 = d.mapToWorld(d.deNormalize(p1));
                    p2 = d.mapToWorld(d.deNormalize(p2));

                    g2d.draw( new Line2D.Float(p1, p2) );
                }
                
            } else {
                g2d.setColor(Color.RED);
                Point.Float ulc = d.getUpperLeftCorner();
                ulc = d.mapToWorld(ulc);
                g2d.draw( new Rectangle2D.Float( ulc.x, ulc.y,
                        d.getDiameter(), d.getDiameter()) );
            }  
        }

        g2d.dispose();
    }
        
    public void showImageJFrame(BufferedImage image) {
        Icon icon = new ImageIcon(image);
        JLabel label = new JLabel(icon);

        final JFrame f = new JFrame("AllerJ");
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.getContentPane().add(label);
        f.pack();
        SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        f.setLocationRelativeTo(null);
                        f.setVisible(true);
                    }
                }
        );
    }
    

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton btnAnalyze;
    private javax.swing.JButton btnBrowse;
    private javax.swing.JCheckBox cbShowBBox;
    private javax.swing.JCheckBox cbShowBlobs;
    private javax.swing.JCheckBox cbShowGrid;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel12;
    private javax.swing.JLabel jLabel13;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextArea logArea;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JSpinner spinCircleSimilarity1;
    private javax.swing.JSpinner spinCols;
    private javax.swing.JSpinner spinColsToAnalyze;
    private javax.swing.JSpinner spinMaxArea;
    private javax.swing.JSpinner spinMinArea1;
    private javax.swing.JSpinner spinRefThreshold;
    private javax.swing.JSpinner spinRows;
    private javax.swing.JSpinner spinRowsToAnalyze;
    private javax.swing.JSpinner spinSquareSimilarity;
    private javax.swing.JSpinner spinStartThreshold;
    private javax.swing.JSpinner spinSteps;
    private javax.swing.JSpinner spinStopThreshold;
    private javax.swing.JTextField textFieldImagePath;
    // End of variables declaration//GEN-END:variables

    private JDialog aboutBox;
    private Logger logger;
    private Logger allerJlogger;
}
